; Object Display Code.
;
; alternative to using MC version.
;
; M.J.Austin Sept/Oct/Nov 1988
;
; Copyright (C) 1988 Level 9 Computing.
;
;
;******* find out why EmergencyKill is needed.
;>>Mike 30/3/89 - optimization in FindObjectNumber added
;   not yet tested in game.

begin

;----

.MCDrawObjectdv1
 gosub @SuspendTaskSwap
 push v1
 push v2
 push v3
 push v4
 push v5
 push v6
  v1=dv1
  v2=dv2
  v3=dv3
  v4=dv4
  v5=dv5
  v6=dv6
  gosub @MCDrawObjectv1Vec
 pop v6
 pop v5
 pop v4
 pop v3
 pop v2
 pop v1
 gosub @ResumeTaskSwap
 return
;-----
.InitACBs
 &ACBList(ACBSortStart)=c0 ; kill displayed acbs
 dx1=8
 dx2=ACBSortStart
 add dx1,dx2
 &ACBList(dx1)=c0 ; zero object to display
 ix4=ACBStart
.IACBLoop
 &ACBList(ix4)=c0
; ix1=ACBSize
 add ix4,c2 ; ix1
 if ix4<ACBEnd then IACBLoop 
 return
;---
;-----
.GetObjectType
; find out what type of object is appropriate to ObjectNumber
; return ObjectNumberType=raster, xzh, compressed or cell
 ObjectNumberType=NeoType
 if ObjectNumber=NeoObjectNumber then GOT1
 ObjectNumberType=XZHType
 if ObjectNumber<MinRaster then GOT1
 ObjectNumberType=RasterType
 if ObjectNumber<MinAnimation then GOT1
 ObjectNumberType=AnimationType
 if ObjectNumber<MinCompressed then GOT1
 ObjectNumberType=CompressedType
 if ObjectNumber<MinCell then GOT1
.GOT2
 ObjectNumberType=CellType
.GOT1
 return
;---
;---
.DisplayCell
; display cell #dv1 at x=dv2, z=dv3, h=dv4
cif Editor
 if dv2<MaxX then DisplayCell1
 if dv2>32767 then DisplayCell1 ; off screen
; calculate  the maximum x used (for xsize of xzh objects)
 MaxX=dv2

.DisplayCell1
 if dv2>MinX then DisplayCell2
 MinX=dv2

.DisplayCell2
;cif Editor
; sub dv3,YBase
; and dv3,DisplayZMask
; add dv3,YBAse
;cend

 if dv2<xclipmin then @DCRet
 if dv2>xclipMax then @DCRet
 dx1=dv3
 sub dx1,dv4
 if dx1<yclipmin then @DCRet
 if dx1>yclipMax then @DCRet
cend

push dv3
 if UseMC=true then DCMC
 if dv5=1 then @DCDraw
.DCMC
; use MC to do plotting
 if dv2>320 then DCMCDrawEnd ; x too big, or negative
;; if dv2<16 then DCMCDrawEnd ; x wrapped round to rhs of screen

 dx1=dv3
 sub dx1,dv4
 if dx1>200 then DCMCDrawEnd ; y too big, or negative
 gosub @MCDrawObjectdv1

.DCMCDrawEnd
pop dv3
.DCRet
 return

.DCDraw
; Use my draw cause it doesn't do vm
; calc y coord = z-h
 if dv2>320 then DCDrawEnd ; x too big, or negative
 sub dv3,dv4
 if dv3>200 then DCDrawEnd ; y too big, or negative
 sub dv1,MinCell
; only used from structure editor, so no conflict over
; use of v1-3 with other tasks.
 v1=dv1
 v2=dv2
 v3=dv3
 gosub @MCNoClipSpriteVec ; displays at x=v2, y=v3
.DCDrawEnd
pop dv3
 return
;---
.DONNeo
; display the Neochrome screen
; which starts at List18(NeoScreenOffset)
; first 128 bytes are palette info.
; subsequent are groups of 4 words, 16 pel per word

; Find starting address (i.e. ScrollX,ScrollY 16x16 blocks
; of PixelScrollX,PixelScrollY pixels)

cif Editor
 dx1=PixelScrollY ; *160
 dx2=160
 gosub @Multdx1dx2
 dx2=ScrollX
 add dx2,dx2
 add dx2,dx2
 add dx2,dx2 ; *8
 add dx1,dx2
 dx2=NeoScreenOffset
 add dx1,dx2
 dx2=128 ; skip palette info
 add dx1,dx2
; dx1 is now offset within List18 of the Neo screen
; to use for Import/export functions
; display it!
; copy neo picture to screen...
;code -
;code +
 &WordWS(WordCursorXPos)=c0
 &WordWS(WordCursorYPos)=c0
 v1=19 ; logical base in list 19
 v2=20 ; physical base in list 20
 gosub @MCCalcScreenAddressVec ; get List19 as start of screen
 v1=18 ; source for copy is list18
 v2=dx1 ; offset within source list
 v3=19 ; destination is list19
 v4=0 ; offset within dest list
 v5=8000 ; no. of words to copy
 if SingleScreen=false then @MCCopyVec
 v5=16000 ; copy a full screen
 goto @MCCopyVec ; gosub, return
cend
 return

;---
.ADXAniCursor
; display xzh cursor (which is an animation object)
cif editor
 push ObjectNumber
  ObjectNumber=CursorObject
  dv1=CursorObject
  gosub @FindObjectNumber
  gosub @SetUPAcb
 pop ObjectNumber
cend
 return
;--------
.DisplayXZHCursor
 cif Editor
; if dv3<xzhCursorZ then @DXZHNotCursorYet
; if dv3>xzhCursorZ then AbsDisplayXZHCursor
;code -
; random dx1
;code +
; if dx1>128 then @DXZHNotCursorYet
 return ; always display cursor on top nowadays.
 cend

.AbsDisplayXZHCursor
cif Editor
 if WantToDisplayCursor=false then @DXZHNotCursorYet
 WantToDisplayCursor=false
 if CursorObject<MinCompRessed then ADXCOk
 if CursorObject<MinCell then @ADXCRet ; don't use ani, compressed as cursor

.ADXCOk
; display cursor in amongst all the other junk
  push size
  push header
  push ScrollX
  push ScrollY
  push dx4
  push dv1
  push dv2
  push dv3
  push dv4
  push dv6 ; refleciton flag
   ScrollX=0
   ScrollY=0
   dv1=CursorObject
  dv2=xzhCursorX
  sub dv2,PixelScrollX
  dv3=xzhCursorZ
;  dx1=zmask ; 65520 ; $fff0
;  and dv3,dx1 ; mask off.
  sub dv3,PixelScrollY
  dv4=xzhCursorH
  sub dv4,CurrentYBase
  if dv1=0 then NullCursor
 push dv6
  dv6=XReflection
  if dv3<MinAnimation then CursorNotAni
  gosub @ADXAniCursor
  goto NullCursor

.CursorNotAni
   gosub @DrawObjectdv1
 pop dv6

.NullCursor
  pop dv6
  pop dv4
  pop dv3
  pop dv2
  pop dv1
  pop dx4
  pop ScrollY
  pop ScrollX
  pop header
  pop size

.DXZHNotCursorYet
 cend

.ADXCRet
.DONRet
 return
;---
.DisplayXZH
; called with dv2=x coord of LHS of object,
; regardless of whether it's reflected or not.

cif editor
 add NestingDepth,c1
 if NestingDepth>20 then @DONRet
 Entryv1=dv1
cend
 gosub @SpecialXZHObject

push ObjectNumber
 ObjectNumber=dv1
 gosub @FindAndDecode ; returns header as start of header
pop ObjectNumber
; now Header=start of header for this object
 if size=4 then DONRet ; @AbsDisplayXZHCursor ; null object - don't bother
 dx4=4
 add dx4,Header
 if dv6=0 then dxzhLoop
; add on width to get the right-hand side as a base
; (because all the offsets specified in xzh objects are relative
; to what would be the lhs if it was displayed normal-way-round)
 dx1=xsize
 add dx1,dx1
 add dx1,dx1
 add dx1,dx1
 add dx1,dx1
 add dv2,dx1

.DXZHLoop
 push dx4 ; save start of object call
 push dv1 ; current object we're interpreting
 push dv2 ; base x coord
 push dv3 ; base z coord
 push dv4 ; base h coord
 push dv6 ; save reflection flag
 push EntryV1
 push xsize
 push size
 push header
 push ObjectNumber
  dx1=StructureBuffer(dx4) ; x coord
  gosub @SignExtenddx1
  add dx1,dx1
  add dx1,dx1
  add dv2,dx1
  if dv6=0 then DXZHNotReflected
  sub dv2,dx1 ; double subtract (because we've already added it on)
  sub dv2,dx1

.DXZHNotReflected
  add dx4,c1
  dx1=StructureBuffer(dx4) ; z coord
  gosub @SignExtenddx1
  add dx1,dx1
  add dx1,dx1
  add dv3,dx1
  add dx4,c1
  dx1=StructureBuffer(dx4) ; h coord
  gosub @SignExtenddx1
  add dx1,dx1
  add dx1,dx1
  add dv4,dx1
  add dx4,c1 
  dx5=header
  add dx5,size
  if dx4>dx5 then @DXZHNull ; off end of buffer

; sixteen bit non word-aligned read...
   dx1=StructureBuffer(dx4)
 add dx1,dx1
 add dx1,dx1
 add dx1,dx1
 add dx1,dx1
 add dx1,dx1
 add dx1,dx1
 add dx1,dx1
 add dx1,dx1
 add dx4,c1
 dv1=StructureBuffer(dx4)
 add dv1,dx1 ; merge in high byte

; was the current xzh object reversed?
; the xzh coords we've calculated above give the RHS
; of the object to call (because offsets are relative
; to this: when non-reflected, it's the LHS),
; but we need to calculate the LHS
; for the benefit of the object we call.
; So take off the x size of the object we're calling.
 if dv6=0 then dxzh5
; i.e. calculate lhs of object before calling it.
 ObjectNumber=dv1
 dx1=32767
 and ObjectNumber,dx1
 push dx4
  gosub @FindAndDecode
 pop dx4
 dx1=xsize
 add dx1,dx1
 add dx1,dx1
 add dx1,dx1
 add dx1,dx1
 sub dv2,dx1 ; calc lhs of the current object for the benefit
; of the called routine

.dxzh5
  if dv1<32768 then DXNotSetReflect
  dx1=65535 ; toggle all bits
  xor dv6,dx1 ; toggle reflection on/off

.DXNotSetReflect
  dx1=32767
  and dv1,dx1 ; remove x reflection bit
 cif Editor
  gosub @EditorCursorStuff
 cend
  if dv1=0 then @DXZHNull ;***
  gosub @DrawObjectdv1 ; beware - recursive!!!

.DXZHNull
 pop ObjectNumber
 pop header
 pop size
 pop xsize
 pop EntryV1
 pop dv6 ; reflection flag
 pop dv4
 pop dv3
 pop dv2
 pop dv1
 pop dx4
 dx1=5
 add dx4,dx1
 dx5=header
 add dx5,size
 if dx4<dx5 then @DXZHLoop
;cif Editor
; push MaxX
; push MinX
; push xsize
;  gosub @AbsDisplayXZHCursor ; if not already displayed
; pop xsize
; pop MinX
; pop MaxX
;cend

.DXZHDown
 if NestingDepth>32008 then DXZHNotUp
 sub NestingDepth,c1
.DXZHNotUp
 return
;--------
cif editor
.EditorCursorStuff
 if Entryv1<>XZHbaseObjectNumber then DXZHNotBase
; flash current object ( selected by TAB)
 add XZHSequenceNumber,c1
 if XZHSequenceNumber<>XZHCurrentItem then DXZHNotBase
 if ybase<>CurrentYbase then dxzhNotBase
 CurrentCellNumber=dv1 ; value of current item - for display on editor screen
 CurrentX=dv2
 add CurrentX,PixelScrollX
 CurrentZ=dv3
 CurrentH=dv4
 sub CurrentH,PixelScrollY
 CurrentItemAddress=dx4
 sub CurrentItemAddress,c4 ; because dx4 was last byte of item.

 add XZHFlashCounter,c1
 if XZHFlashCounter=1 then DXZHNotBase
 XZHFlashCounter=0
 dv1=0 ; prevent it displaying current item.
 return

.DXZHNotBase
; push MaxX
; push MinX
; push dv6
; push XZHSequenceNumber
;  XZHSequenceNumber=32000 ; prevent flashing things if CursorObject=ObjectNumber
;  gosub @DisplayXZHCursor
; pop XZHSequenceNumber
; pop dv6
; pop MinX
; pop MaxX
 return
cend
;------
.DrawObjectdv1
; display dv1 directly from StructureBuffer
; starting from ScrollX,ScrollY blocks within the object (measured
; from the top left, as always)
; and dv6=0 for normal, -1 for LR reversed.
 if dv1=NeoObjectNumber then @DONNeo
 if dv1<MinAnimation then DONNotAnimation
 if dv1<MinCompressed then @DisplayAnimation

.DONNotAnimation
 AniViewMode=1 ; view future ani objects as animation!
.DON2
 if dv1<MinRaster then SubDisplayXZH
 if dv1<MinCell then DON1
 goto @DisplayCell

.SubDisplayXZH
 push ObjectNUmber
  gosub @DisplayXZH
 pop ObjectNumber
 return

.DON1
; display raster.
 gosub @SpecialRasterObject
;cif editor
; if dv5=0 then DONInsert ; insert into structure, so use Acode.
;; (in fact, I don't know why we should have to do this, but
;; if MC is used to insert rasters containing reversed cells,
;; (as opposed to inserting the whole object, or individual
;; cells), they are displayed non-reversed.)
; if QuickMode=true then @MCDrawObjectdv1
; goto DONInsert
;cend

 goto @MCDrawObjectDv1 ; grunge demo crashes without this ****.

.DONInsert
push ObjectNumber
 ObjectNumber=dv1
 gosub @FindAndDecode ; returns dx4 as start of header
pop ObjectNumber
; now Header=start of header for this object
 if size=4 then @DONRet ; null object - don't bother

 dx4=0
cif Editor
 dx1=ScrollY
 dx2=xsize
 gosub @Multdx1dx2
 add dx1,dx1 ; *2 because word-based table
 add dx1,ScrollX
 add dx1,ScrollX ; *2 because word-based table
 dx4=dx1
cend
 add dx4,Header
 add dx4,c4
; dx4=start of data area for object

cif Editor
 dx1=1
 and dx1,dx4
 if dx1=0 then DONOk
 return ; somehow got an odd coord

.DONOk
cend
cif editor
 y=ScrollY
cend

.DONYLoop
cif editor
 x=ScrollX
 if CurrentMapMode=true then @DONRasterMap
cend

 push dv1
 push dv2
 push dx4 ; save start of line
 if dv6=0 then DONXLoop ; no reflection
; move origin for raster along to right hand side of raster
 dx1=xsize
 add dx1,dx1
 add dx1,dx1
 add dx1,dx1
 add dx1,dx1
 add dv2,dx1
 dx1=16
 sub dv2,dx1 ; we have right hand coord of rightmost cell,
; need left hand.

.DONXLoop
  if x>32768 then DONNull ; -ve scroll
  if y>32768 then DONNull ; -ve scroll
  &dv1=StructureBuffer(dx4)
  if dv1=0 then DONNull ;*
  if dv1=32768 then DONNull
  push dv2 ; x
  push dv3 ; z
  push dv4 ; h
  push dv6 ; reflection flag
  push dx4
   if dv1<32768 then DONRasterNotReflect
   dx1=32768
   xor dv6,dx1 ; invert reflection flag
   sub dv1,dx1 ; strip off reflection flag
.DONRasterNotReflect
   gosub @DrawObjectdv1 ; display object dv1. MCNoClipSprite
  pop dx4
  pop dv6
  pop dv4
  pop dv3
  pop dv2
.DONNull
  add dx4,c2
  dx1=16
  add dv2,dx1 ; x coord
  if dv6=0 then DONNoReflect
  sub dv2,dx1 ; x coords decrease in x reflection mode
  sub dv2,dx1

.DONNoReflect
  add x,c1
  if x>32768 then @DONXloop
  if x<xsize then @DONXloop
; Next Y
 pop dx4
 pop dv2 ; starting x coord
 pop dv1
 add dx4,xsize
 add dx4,xsize ; add on double because word-based table
 dx1=16
; add dv3,dx1 ; y coord
 sub dv4,dx1 ; h coord
 add y,c1
 if y>32768 then @DONYLoop
 if y<ysize then @DONYLoop
.DCOEnd
 return
;----
cif Editor
.DONRasterMap
; display raster dv1 at coords (dv2,dv3)
; in Map form.
; if ysize<1 then @DONRMRet ; ignore collision
 push dv2
 push dv3
 asr dv2 ; halve coords, because map uses 8x4 not 16x16
 asr dv3
 asr dv3 ; quarter y coord, because map uses 8x4, not 16x16
 y=ScrollY

.DONMapYLoop
 x=ScrollX
 push dv2
 push dv3
.DONMapXLoop
   gosub @DONMapBlock
  dx1=8 ; reduced from normal 16
  add dv2,dx1 ; x coord
  add x,c1
  if x>32768 then @DONMapXloop
  if x<xsize then @DONMapXloop
; Next Y
 pop dv3 ; starting y coord
 pop dv2 ; starting x coord
 if zsize=0 then DONRMEnd
 dx1=4 ; reduced from normal 16
 sub dv3,dx1 ; reduce z coord
 add y,c1
 if y>32768 then @DONMapYLoop
 if y<zsize then @DONMapYLoop ; display z size, because this is an overhead view
; pop v3
; pop v2
; pop v1

.DONRMEnd
 pop dv3
 pop dv2

.DONRMRet
 return
;---
.DonMapBlock
  &WordWS(WordCursorXPos)=dv2
;  dx1=dv3
;  sub dx1,dv4
  if dv3>yclipMax then DONRMRet ; off window, or negative
  &WordWS(WordCursorYPos)=dv3 ;x1
  v1=19 ; logical base in list 19
  v2=20 ; physical base in list 20
  gosub @MCCalcScreenAddressVec ; get List19 as current address
; plot an 8x4 pixel square
  dx1=136 ; $10001000
  if ysize<3 then DON3
  dx1=170 ; $10101010
  if ysize<5 then DON3
  dx1=255 ; $11111111
.DON3
 push dx1
  dx1=dv3 ; y coord in pixels (8x4 pels per block).
  dx2=YRoomOffset
  asr dx2
  asr dx2
  sub dx1,dx2 ; reduce y by offset used when it's displayed
  if dx1>MapYPixels then DONOffCDMap
  dx2=10 ; Multiply by 10 to give 20 words per line
  gosub @MultDx1Dx2
  dx2=dv2 ; x coord in pixels
  dx3=XRoomOffset
  asr dx3
  sub dx2,dx3 ; reduce x by offset used when it's displayed
  if dx2>MapXPixels then DONOffCDMap
  asr dx2 ; /4 -> 0..160 (8x4s) => 0..40 per line (8x4 pels per block)
  asr dx2
  add dx1,dx2 ; add on x blocks
  dx2=65534 ; remove bottom bit
  and dx1,dx2
  &Map(dx1)=ysize
.DONOffCDMap
 pop dx1

  list19(0)=dx1
  list19(160)=dx1
  if zsize=0 then @DON4Ret ; draw a thinner bar if 0 size.
  list19(320)=dx1
  list19(480)=dx1
;  list19(640)=dx1
;  list19(800)=dx1
;  list19(960)=dx1
;  list19(1120)=dx1
  list20(0)=dx1
  list20(160)=dx1
  list20(320)=dx1
  list20(480)=dx1
;  list20(640)=dx1
;  list20(800)=dx1
;  list20(960)=dx1
;  list20(1120)=dx1
.DON4Ret
 return


cend

;---
;.DisplayAnimationFromMC
;; &dv1=longWs(Longdv1)
;; &dv2=longWs(Longdv2)
;; &dv3=longWs(Longdv3)
;; &dv4=longWs(Longdv4)
;; &dv5=longWs(Longdv5)
;; &dv6=longWs(Longdv6)
; &header=WordWS(WordAniHeader)
; &dv2=WordWS(WordX)
; &dv3=WordWS(WordZ)
; &dv4=WordWS(WordH)
; gosub @SetUpACB
; return
;---
.DisplayAnimation
; display animation Object dv1 in text-form from
; start of current edit window (ScrollY)
; with the current edit line (CurrentAnimationY) highlighted.
 if ACBSOnly=true then DARet ; ignore Ani objects
 ObjectNumber=dv1
 gosub @FindAndDecode
 if AniViewMode=0 then @TextDisplayAnimation
  gosub @SetUpACB ; PreviewAnimation

.DARet
 return
;-------
.DisplayACBs
; display all Animation Control Blocks.
 dv5=1 ; display as sprite. 
.DisplayACBsdv5
 CurrentStore=ACBSortStart
 &ACBList(CurrentStore)=c0 ; indicate end of sorting list

.DisplayACBs2

; GMJ 1/9/89
cif NotEditor
; display still people while in menu mode
 if ACBStart<>ACBSearchStart then DisplayACBs3
 gosub @DisplayStillPeople
.DisplayACBs3
cend

 dv6=0 ; non-reversed
 ACBHeader=ACBStart
.DisplayACBsLoop
 ACBLoopBreaker=0
 gosub @DisplayACB
 dx1=ACBSize
 add ACBHeader,dx1
 if ACBHeader<ACBEnd then DisplayACBsLoop
.DACBRet
 return
;----
cif NotEditor
.DisplayStillPeople
; display Animation Control Blocks.
; between 0 and ACBStart
 CurrentStore=ACBSortStart
 &ACBList(CurrentStore)=c0 ; indicate end of sorting list

 dv6=0 ; non-reversed
 ACBHeader=ACBSize
.DisplaySPLoop
 ACBLoopBreaker=0
 gosub @DisplayStillPerson
 dx1=ACBSize
 add ACBHeader,dx1
 if ACBHeader<ACBStart then DisplaySPLoop
 return
cend
;---
.DisplayStillPerson
 &dv1=ACBList(ACBHeader)
 if dv1=0 then DACBRet
 dx4=ACBHeader
 add dx4,c2
 &SP=ACBList(dx4)
 if SP=0 then DSPRet
 dx1=62
 add dx1,ACBHeader
 &Num=ACBList(dx1)
 add dx4,c2
 &dv2=ACBList(dx4)
 add dx4,c2
 &dv3=ACBList(dx4)
 add dx4,c2
 &dv4=ACBList(dx4)
 gosub @StoreNum
.DSPRet
 return
;---
.DisplayACB
; ** dv1 ( object number which set up the ACB )
; ** SP  ------------------------- (relative to start of header)
; ** x (pixels)  }                |
; ** z (pixels)   } current base coords for this ani object.
; ** h (pixels)  }                |
; AA CC EE Stack entries 	  |
; AA CC	EE 			  |
; AA CC	EE 	             <----

; Note - does not ACTUALLY display anything nowadays -
; it merely feeds x,z,h and object nums into
; ACBList(CurrentStore) which is later sorted and displayed -
; i.e. to get the prioritization correct.
 &dv1=ACBList(ACBHeader)
 if dv1=0 then @DACBRet
 dx4=ACBHeader
 add dx4,c2
 &SP=ACBList(dx4)
 if SP=0 then @DACBRet

 add dx4,c2
 &dv2=ACBList(dx4)
 add dx4,c2
 &dv3=ACBList(dx4)
 add dx4,c2
 &dv4=ACBList(dx4)
 if SP>32010 then @DisplayStaticACB
 add SP,ACBHeader ; make absolute

.DisplayACB1
 add ACBLoopBreaker,c1
 if ACBLoopBreaker>20 then @RDARet

 dx1=1
 and dx1,sp
 if dx1<>0 then @RDAEmergencyKill ;***** why is this necessary?

 dx1=SP
 &AA=ACBList(dx1) ; find AA at current stack position
 add dx1,c2
 &CC=ACBList(dx1) ; find CC at current stack position
 add dx1,c2
 &EE=ACBList(dx1) ; find EE at current stack position

 dv6=cc ; extract reversal status (top bit of count) >>Mike 23/9/89
 dx1=32767 ; $7fff
 and cc,dx1
 dx1=32768
 and dv6,dx1 ; keep top bit only

 if AA<EE then @RDAOk
.RDAReturn
; off end of list - do a return
 dx1=6
 sub sp,dx1
 dx1=sp
 sub dx1,ACBHeader
 if dx1>ACBStackStartMinus1 then RDAPop2
; Nothing else to pop: terminate this ACB
.RDAKill
 gosub @SpecialACBKilled
 &ACBList(ACBHeader)=c0
 dx1=2
 add dx1,ACBHeader
 &ACBList(dx1)=c0 ; kill SP as well, just in case acb is reanimated
 return

.RDARet
; break ;****
; dx1=StructureBuffer(AA) ;****
; &dx1=ACBList(SP) ;***********
.RDANull
 return

.RDAPopKill
 pop dx1
 goto RDAKill

.RDAEmergencyKill
; break ;*
 goto RDAKill

.RDAPop2
 dx4=2
 add dx4,ACBHeader
; dx1=sp
 sub sp,ACBHeader ; make it relative
 &ACBList(dx4)=sp
 add sp,ACBHeader ; make it absolute again
 goto @DisplayACB1 ; try again - with the caller of this routine
;---
.RDAOk
 gosub @DecodeInstruction
; -> dx4=start of next instruction
; -> Opcode, x,z,h,num as the instruction to execute
 if dx4>EE then @RDAReturn ; padding instruction at end.
 if cc=24373 then SkipInstruction
 if opcode=128 then NextInstruction ; repeating a repeat instruction
 if cc<>0 then RDARepeat
.NextInstruction
; execute instruction once, then skip onto next.
; (In fact, let's skip onto next now)
 &ACBList(sp)=dx4 ; set up new pc
 goto DANoRepeat

.SkipInstruction
;;break ;*******
 &ACBList(sp)=dx4 ; set up new pc
 dx1=2
 add dx1,sp
 &ACBList(dx1)=dv6 ; zero repeat count so future instructions are done
 goto @DisplayACB ; repeat it no times at all.

.RDARepeat
; decrement repeat count
 if cc=32767 then DANoRepeat ; repeat forever
 dx1=2
 add dx1,sp
 sub cc,c1
 dx2=cc ;>>Mike 23/9/89
 or dx2,dv6
 &ACBList(dx1)=dx2 ; cc
 
.DANoRepeat
; execute instruction @ StructureBuffer(AA).
;; gosub @DecodeInstruction
 if Opcode<>192 then @RDANotFork
 if num=0 then @RDANull
 ObjectNumber=num
 gosub @GetObjectType
 if ObjectNumberType<>AnimationType then @RDANotFork2
 gosub @FindAndDecode
 gosub @SetUpACB0 ; just display it, don't step on ani or anything.
 goto @DisplayACB ; display next frame of parent as well

.RDANotFork2
 return
;---
.RDANotFork
 if Opcode<>0 then @RDANotDisplay
 if num=0 then @RDANull
 if num<>16383 then RDANotFlip
; flip xreflection
 dx2=32768
 xor dx2,dv6
 dx1=2
 add dx1,sp
 &ACBList(dx1)=dx2 ; dv6
 goto @DisplayACB
;---
.RDANotFlip
 ObjectNumber=num
 gosub @GetObjectType
 if ObjectNumberType<>AnimationType then @RDANotAni
; if num<MinAnimation then @RDANotAni
; if num<MinCell then RDAAnimation
; goto @RDANotAni
;
;.RDAAnimation
; for calling an animation type, want to create a new PC entry
; on the stack, so the subroutine will be executed instead
; ACBList(SP) is current PC structure
; SP=ACBList(ACBHeader+2) is sp
 dx1=6
 add sp,dx1 ; skip onto next entry
; if sp<>ACBStackStart then RDANoBug2 ;****
; gosub @SuspendTaskSwap ;****
; break ;****
; gosub @ResumeTaskSwap ;*****
;
;.RDANoBug2
push sp
 dx2=2
 add dx2,ACBHeader
 dx1=sp
 sub dx1,ACBHeader ; make sp relative
 if dx1>ACBStackEnd then @RDAPopKill
 &ACBList(dx2)=dx1 ; set up new sp
 gosub @FindObjectNumber
; AA CC EE Stack entries
 dx2=4
 add dx2,header ; skip over header
 &ACBList(sp)=dx2
 add sp,c2
 &ACBList(sp)=dv6 ; zero count, including reversal flag. c0 ; single execution of next instruction
 add sp,c2
 &dx1=StructureBuffer(header)
 add dx1,header
 &ACBList(sp)=dx1 ; end of object
pop sp ; new sp
 goto @DisplayACB1 ; display it immediately

.RDANotAni
; Store request to draw object num at coords dv2,dv3,dv4
; dx1=ACBRasterOffset
; add dx1,ACBHeader
; &dx1=ACBList(dx1)
 gosub @SpecialAniObject

.StoreNum
 dx1=62
 add dx1,ACBHeader
 &ACBlist(dx1)=num ; store frame being used.

 dx1=ACBRasterOffset
 add dx1,ACBHeader
 &dx1=ACBList(dx1)

 add CurrentStore,c2 ; skip link ptr
 &ACBList(CurrentStore)=dv3 ; store z coord first, for easy sorting
 add CurrentStore,c2
 &ACBList(CurrentStore)=dv2 ; x coord
 add CurrentStore,c2
 &ACBList(CurrentStore)=dv4 ; h coord
 add CurrentStore,c2
 &ACBList(CurrentStore)=num ; object to draw
 add CurrentStore,c2
 &ACBList(CurrentStore)=dv6 ; reversal flag
 add CurrentStore,c2
 &ACBList(CurrentStore)=dx1 ; raster offset
 add CurrentStore,c2
 &ACBList(CurrentStore)=c0 ; zero to indicate new end of list
.SADORet
 return

.SortAndDisplayObjects
; given the complete list, ACBList(ACBSortStart..CurrentStore),
; sort it and display from the back, working forwards.
; Format of each entry is:
;  ACBList(CurrentStore+0)=link ptr for sort.
;  ACBList(CurrentStore+2)=z coord
;  ACBList(CurrentStore+4)=x coord
;  ACBList(CurrentStore+6)=h coord
;  ACBList(CurrentStore+8)=number of object to plot.
;  ACBList(CurrentStore+10)=reflection flag to be used
;  ACBList(CurrentStore+12)=RasterOffset
 dv5=1 ; only every display as sprites...

.SortAndDisplaydV5
; A linked list sort...
 dx6=ACBSortStart ; start of current linked list.
 dx5=14 ; size of each entry
 dx4=ACBSortStart; current entry we're examining
 if CurrentStore=dx4 then SADORet ; nothing to display
.LLSort1
 add dx4,c2
 &dx1=ACBList(dx4) ; dx1:=z coord
 sub dx4,c2
 gosub @AddToLL
 add dx4,dx5
 if dx4<CurrentStore then LLSort1
;
; now display them all...
; Starting with ACBList(dx6)
.LLDisplayLoop
 push dx6
  add dx6,c2
  &dv3=ACBList(dx6) ; z coord
  add dx6,c2
  &dv2=ACBList(dx6) ; x coord
  add dx6,c2
  &dv4=ACBList(dx6) ; h coord
  add dx6,c2
  &dv1=ACBList(dx6) ; object number
  add dx6,c2
  &dv6=ACBList(dx6) ; reversal flag
  add dx6,c2
  &dx1=ACBList(dx6) ; raster offset
  &WordWS(WordRasterOffset)=dx1
;   dv5=1 ; only every display as sprites...
  gosub @MCDrawObjectDv1
  &WordWS(WordRasterOffset)=c0
 pop dx6
 &dx6=ACBList(Dx6)
 if dx6<>0 then @LLDisplayLoop
 return

.AddToLL
; find where in current linked list: ACBList(dx6) we
; should add the object ACBList(dx4), which has z coord dx1,
; and do it.
 dx3=dx6
 add dx3,c2 ; move on to z coord
 &dx2=ACBList(dx3) ; get z coord of first item in linked list.
 if dx1<dx2 then @AddToStart ; ACBList(dx4) becomes the first entry
; not behind the first item we have at present,
; so follow the linked list down until it pops out in front
; of an existing item, and behind the next one in the chain
 push dx6 ; save pointer to first item in LL - won't change it now.
 dx7=0
.AddToLLLoop
  add dx6,c2
  &dx2=ACBList(dx6) ; dx2:=z coord of object (dx6) in chain
  sub dx6,c2
  &dx3=ACBList(dx6) ; where is next entry in list?
  if dx3=0 then @AddIn ; end of existing chain - add at end
  add dx3,c2
  &dx3=ACBList(dx3) ; dx3:=z coord of next object in chain
  if dx1<dx2 then NotYet ; haven't passed first object
  if dx1<dx3 then @AddAfterDx6 ; between (dx6) and next one
.NotYet
  &dx6=ACBList(dx6) ; step on to next item in LL
  goto @AddToLLLoop

.AddAfterDX6
  &dx3=ACBList(dx6)

.AddIn
; ACBList(Dx4) fits in between ACBList(dx6) and ACBList(dx3)
 if dx6=dx4 then AddInEnd ; first item, which forms start and end
; of what will become the chain.
 &ACBList(dx4)=dx3 ; entry in list which follows this one
 &ACBList(dx6)=dx4 ; entry in list which preceeds this one
.AddInEnd
 pop dx6 ; get back the start of the Linked list
 return
;---
.AddToStart
 dx1=dx6 ; old start of linked list
 dx6=dx4
 if dx6=dx1 then ATS1 ; first entry to be put in an empty LL will
; always go wrong unless we do this.
 &ACBList(dx6)=dx1 ; point to old start of linked list.
.ATS1
 return
;---
.RDANotDisplay
 if Opcode<>64 then @RDANotShift
; 01HHHHHH XXXXXXXX ZZZZZZZZ = shift base for current object. (pixels)
; x,z,h are treated as signed bytes
 dx1=x
 gosub @SignExtenddx1
 push dx1 ; push x
  dx1=z
  gosub @SignExtenddx1
  push dx1 ; push z
   dx1=h
   gosub @SignExtend6Bitdx1
   dx4=dx1 ; h speed
  pop dx3 ; z speed
 pop dx2  ; x speed
 gosub @SpecialAniShift ; moving dv1 from dv2,dv3,dv4 by dx2,dx3,dx4
 if dv6=0 then RDAShiftNonReflected
 sub dv2,dx2 ; when reflected, horizontal shifts have opposite effect
 goto RDADoneHorizontal
.RDAShiftNonReflected
 add dv2,dx2

.RDADoneHorizontal
 add dv3,dx3
 add dv4,dx4

; write current base back to xzh base
 dx4=4
 add dx4,ACBHeader
 &ACBList(dx4)=dv2
 add dx4,c2
 &ACBList(dx4)=dv3
 add dx4,c2
 &ACBList(dx4)=dv4
 goto @DisplayACB

.RDANotShift
 if Opcode<>128 then @RDANotRepeat
; 10RNNNNN		     = If R=0, repeat next instruction N times
;			          R=1, repeat next until 5bit random>N
; change ACBList(SP+2) to reflect desired count
 dx1=2
 add dx1,sp
 dx2=32767 ; signify repeat forever
 if num=0 then @DARepeatDx2

 add num,c1 ; repeat goes 2..16, not 1..15 (0=forever)
 if num<17 then RDANotRandom
; 17 is lowest value that comes here for random.
; -> we want that to translate to "do never"
 dx2=18
 sub num,dx2 ; makes num negative for 17
code -
 random x2
code +
 asr x2
 asr x2
 asr x2
 asr x2
; have random 0..15
 sub num,x2
 if num<32000 then GotRandomRepeat
; skip next instruction...
 dx2=24373 ; skip next instruction
 goto DARepeatDX2

.GotRandomRepeat
.RDANotRandom
; add on to existing count
 &dx2=ACBList(dx1)
 add dx2,num
.DARepeatDx2
 or dx2,dv6
 &ACBList(dx1)=dx2 ; num
 goto @DisplayACB ; execute the instruction to be repeated

.RDANotRepeat
 return
;----
.DecodeInstruction
; For instruction StructureBuffer(AA), return
; dx4=address of next instruction
; and opcode, x, z, h, num as the instruction
 dx4=AA
 opcode=StructureBuffer(AA)
 dx1=opcode ; save number
 dx2=192
 and opcode,dx2
; now opcode=one of:
; 00NNNNNN NNNNNNNN = DISPLAY object N
; 01HHHHHH XXXXXXXX ZZZZZZZZ = shift base for current object. (pixels)
; 10RNNNNN		     = If R=0, repeat next instruction N times
;			          R=1, repeat next until 5bit random>N
; 11NNNNNN		     = fork (start new animation object
;  in addition to existing one)

 if opcode=192 then DIDisplay
 if opcode<>0 then @DINotDisplay
.DIDisplay
; dx2=256
; gosub @Multdx1dx2 ; high byte of num
 add dx1,dx1
 add dx1,dx1
 add dx1,dx1
 add dx1,dx1
 add dx1,dx1
 add dx1,dx1
 add dx1,dx1
 add dx1,dx1

 num=dx1
 add dx4,c1
 dx1=StructureBuffer(dx4)
 add num,dx1
 add dx4,c1
push dx1 ; I'm not sure if this is necessary or not.
 dx1=16383 ; $3fff
 and num,dx1 ; remove opcode (for Fork opcode)
pop dx1
; dx1=StructureBuffer(dx4)
; h=15
; and h,dx1
; dx2=16
; gosub @Divdx1dx2
; x=dx1
; add dx4,c1 ; skip on to next instruction
; z=0 ; no z offset generated by this instruction
 return
;
.DINotDisplay
 if opcode<>64 then DINotShift
; 01HHHHHH XXXXXXXX ZZZZZZZZ = shift base for current object. (pixels)
 h=63
 and h,dx1
 add dx4,c1
 x=StructureBuffer(dx4)
 add dx4,c1
 z=StructureBuffer(dx4)
 add dx4,c1
 return
;
.DINotShift
 if opcode<>128 then DINotRepeat
; 10RNNNNN		     = If R=0, repeat next instruction N times
;			          R=1, repeat next until 5bit random>N
 num=63
 and num,dx1
 add dx4,c1
 return
;
;code -
; prs " bad instruction: " ;*******
;code +
.DINotRepeat
;;break ; test code
 add dx4,c1 ; prevent loops and sim if we get a bad instruction.
 return

;--------
.SetUpACBdv1
 ObjectNumber=dv1
 gosub @FindAndDecode

.SetUpACB
; Given dv1=Object, dv2=x, dv3=z, dv4=h, dv5=placement type:
; and header as start of Object
; Set up an ACB of the form:
; ** dv1 (object number)
; ** SP  ------------------------- (relative to itself)
; ** x (pixels)  }                |
; ** z (pixels)   } current base coords for this ani object.
; ** h (pixels)  }                |
; (Other stuff, depending on value of ACBStackStart in
; the rest of the program)	  |
;				  |
; AA CC EE Stack entries 	  |
; AA CC	EE 			  |
; AA CC	EE 	             <----
; 	AA: AA= address of opcode to execute next,
; 	   And CC=Repeat Count for current repeat operation
;	   Each time executed, decrements+does instruction.
;	   If (after decrement) CC=0, we 
;	   move AA on to next instruction: i.e. treated as a PC.
; 	   Then execute the instruction at the original value of AA.
; Essentially, CC is the number of EXTRA times to
; repeat the next instruction: i.e. to execute it once, CC=0
; to execute it twice, CC=1 etc.
; 	EE: is address of first byte after current object.
; Also, return ACBList(dx4) as the ACB set up.
; if dv1<MinAnimation then @SetUpStaticACB
; if dv1<MinCell then @SetUpACBa
; goto @SetUpStaticACB
;
;.SetUpACBa
 gosub @SetUpACB0
; if dx4<32767 then @SetUpACBRet
 push dv5
 push dx4
 gosub @DisplayACBs ; randomize phase of acb's (particularly
; needed when we're displaying a room structure, which
; includes several permament animation sequences. If these
; are set up "in phase", the acode ani display code jerks
; every fourth frame (or whatever the normal sequence length
; is).
 pop dx4
 pop dv5
 return
;-----
.SetUpACB0
 if dv1<MinAnimation then @SetUpStaticACB
 if dv1<MinCell then @SetUpACB1
 goto @SetUpStaticACB

.SetUpACB1
 dx4=ACBSearchStart
.SUALoop
 &dx1=ACBList(dx4)
 if dx1=0 then SUAGotBlankACB1
;; if dx1=dv1 then SetUpACBRet ; already have an ACB for this object
 dx1=ACBSize
 add dx4,dx1
 if dx4<ACBEnd then SUALoop 
 dx4=65520 ; negative error return.
.SetUpACBRet
 return

.SUAGotBlankACB1
; ACBList(dx4) is blank acb
 gosub SUAGotBlankACB
 return
;---
.SUAGotBlankACB
push dx4
 add dx4,c4 ; skip over sp
 &ACBList(dx4)=dv2 ; x
 add dx4,c2
 &ACBList(dx4)=dv3 ; z
 add dx4,c2
 &ACBList(dx4)=dv4 ; h
 add dx4,c2
 &ACBList(dx4)=RasterOffset ; ACBList(ACBRasterOffset)
pop dx4

.SUAChangeACB
; change acb without altering coordinates etc.
 &ACBList(dx4)=dv1 ; poke in object number
 add dx4,c2
 dx1=ACBStackStart
 &ACBList(dx4)=dx1 ; ptr to "sp"
 sub dx4,c2

;&*&*& Change requested by Graham: 19/7/89
 cif NotEditor
  object=dx4
  asr object
  asr object
  asr object
  asr object
  asr object
  asr object
  dx1=ACBObjectOffset
  add dx1,dx4
  &ACBList(dx1)=object
 cend
;&*&*&


push dx4
 dx1=ACBStackStart
 add dx4,dx1 ; x4:=start of stack area.
 dx1=4
 add dx1,header
 &ACBList(dx4)=dx1 ; PC within ani object
 add dx4,c2
 dx1=32768 ; only keep reversal flag
 and dv6,dx1
 &ACBList(dx4)=dv6 ; zero count - top bit is reversal status. >>Mike 23/9/89
 add dx4,c2
 &dx1=StructureBuffer(header)
 add dx1,header
 &ACBList(dx4)=dx1 ; first byte outside current object
pop dx4
 return
;---
.SetUpStaticACBdx4
 gosub @SUAGotBlankACB
 goto @SUS1

.SetUpStaticACB
; set up an ACB for a non-animation object - i.e.
; an object which will remain still, such as a person.
; (Though it may be moved around by special-case code).
; Thus it is effectively used as a sprite control block.
push dv1
 gosub @SetUpACB1
pop dv1
.SUS1
push dx4
 &ACBList(dx4)=dv1
 add dx4,c2
 dx1=34000 ; make sure SP=ACBHeader+dx1 is negative.
 &ACBList(dx4)=dx1 ; flags it as negative
 dx1=ACBRasterOffset
 add dx1,dx4
 &ACBList(dx1)=RasterOffset
pop dx4
 return
;---
;-------
.DisplayStaticACB
; have an ACB to display some object at coords dv2,dv3,dv4
; &v1=ACBList(ACBHeader) ; dx4)
; v2=dv2
; v3=dv3
; v4=dv4
; v5=dv5
; v6=dv6
; goto @MCDrawObjectv1
 &num=ACBList(ACBHeader)
; dx1=0 ; raster offset=0
 goto @StoreNum
;------

;--
cif editor
code -
 prs " FindObjectNumber. "
code +
cend
;---
.FindAndDecode
 gosub @FindObjectNumber
 goto @DecodeHeader
;---
.FindObjectNumber
; return Header=offset of ObjectNumber within StructureBuffer
; goto @FON6
;
 gosub @GetObjectType
 dx1=32767 ; strip off reflection flag
 and ObjectNumber,dx1
 if ObjectNumber=0 then @FON6
 if ObjectNumber>MinCompressed then @FON6
;;; goto @FON6 ;***************
cif Editor
 if StructuresChanged=true then @FON6
 if SafeMode<>false then @FON6
cend
 push v1
 push v2
 push v3
  v1=ObjectNumber ; object to find
  v2=StructureBufferList ; list containing structure buffer
  gosub @MCFindObjVec
; v3 is offset within structure buffer
 header=v3
 dresult=true
 pop v3
 pop v2
 pop v1
 return
;---
.FON6
cif Editor
 if ObjectNumber>MinCell then @FONEndOfBuffer
 LastHeader=65535 ; dummy value for error-trapping
 StartStructureBuffer=32
 if ObjectNumber=0 then @FONEndOfBuffer ; not found
cend

 Header=StartStructureBuffer
 dx2=1 ; current object number

.FON1
cif Editor
 push dx1
 push dx4
  dx1=1 ;test code
  and dx1,header
  if dx1=0 then @FONHeaderOk
  dx1=65534
  and header,dx1 ; make it even
  goto @FONError

.FONHeaderOk
 pop dx4
 pop dx1
cend

 &dx1=StructureBuffer(Header)

 cif Editor
; check validity of pointer
 push dx1
 push dx4
 if AniViewMode=1 then @fonOk ; skip checking in preview mode.
 if SafeMode=false then @fonOk ; skip checking
; if dx1<4 then fonError
 if dx1>32000 then fonError ; don't allow backwards pointers.
 dx4=1 ;test code
 and dx4,dx1
 if dx4=0 then @FONOk
.fonError
 if IgnoreErrors>5000 then fonE2
 sub IgnoreErrors,c1
 goto @FONExit

.fonE2
push dx1
push dx4
  gosub @SetUpPhysicalTextPtr
  &WordWS(WordCursorYPos)=c0
  RealCr=true
  dx4=LastHeader
code -
  prs " A major error has been detected at "
  message cr
  prs " offset "
  print header
  prs " ptr="
  print dx1
  prs " Last header="
  print dx4 ; = LastHeader
  message cr
  prs " within the structure file. "
  message cr
  prs " The system may possibly crash. "
  message cr
  prs " Hit panic or any key. "
  message cr
  prs " Press esc to truncate structure file."
  message cr
  prs " Press '1' to ignore next 100 errors. "
  message cr
  prs " Press '2' to ignore next 5000 errors. "
  message cr
  prs " Press '3' to break. "
code +
 pop dx4
 pop dx1
 RealCr=false
 gosub @WaitKey
 if v2=1 then @FONTruncate
 if v2<>2 then fonNotIgnore ;'1'
 IgnoreErrors=100
 goto @FONExit

.fonNotIgnore
 if v2<>3 then fonNotIgnore2 ;'2'
 IgnoreErrors=4999
 goto @FONExit

.fonNotIgnore2
 if v2<>4 then fonNotBreak ;'3'
 break ; leave in, please
 push LastHeader
 pop LastHeader
 push header
 pop header
 goto @FONExit

.fonNotBreak
 &dx1=StructureBuffer(Header)
 dx4=65534 ; remove bottom bit
 and dx1,dx4 ; attempt to correct tree
 if dx1>3 then FONNotTooSmall
 dx1=4

.FONNotTooSmall
 &StructureBuffer(header)=dx1
 pop dx4
 pop dx1
 goto @fonEndOfBuffer ; and recover gracefully

.FONTruncate
 dx1=1024

 push header
.FONT1
 dx4=65534
 and header,dx4
 &StructureBuffer(header)=c0
 add header,c2
 and dx1,dx4
 if dx1>0 then FONT1
 pop header

.FONExit
cif editor
 LastHeader=header
cend
 dx1=65534
 and header,dx1
 pop dx4
 pop dx1
 goto fonEndOfBuffer ; and recover gracefully

.fonOk
 pop dx4
 pop dx1
 cend

 if dx1=0 then FONEndOfBuffer
 if dx2=ObjectNumber then FONFound
 add dx2,c1 ; increment object number
 add Header,dx1
 goto @FON1

.FONFound
 dresult=true
 return

.FONEndOfBuffer
 dresult=false ; not found
 return
;---
;--
.DecodeHeader
; decode header at Header
 dx4=Header
 &Size=StructureBuffer(dx4)
 add dx4,c2
 xsize=StructureBuffer(dx4)
 add dx4,c1
 zsize=StructureBuffer(dx4)
; now calculate ysize (only valid for raster objects)
 dx1=size
 sub dx1,c4
; halve size (word-based table), then divide it by xsize to give ysize
 if size=0 then dhNullObject
 dx2=xsize
 add dx2,dx2
 gosub @Divdx1dx2
 ysize=dx1
 return

.dhNullObject
; blank object
 ysize=0
 return
;---
.MultdX1dx2
; dx1=dx1*dx2
 dx3=dx1
 if dx2=0 then Multdx1dx2Zero
 if dx2=1 then Multdx1dx2Ret
.Multdx1dx2Loop
 add dx1,dx3
 sub dx2,c1
 if dx2>1 then Multdx1dx2Loop
.Multdx1dx2Ret
 return
.Multdx1dx2Zero
 dx1=0
 return
;---
.SignExtenddX1
; dX1 contains a byte: sign-extend to a full word
 dx2=255
 and dx1,dx2
 if dx1<128 then SignExtenddRet
 dx2=65280 ; 65536-256
 add dx1,dx2
.SignExtenddRet
 return
;---
;---
.SignExtend6Bitdx1
; dx1 contains a 6-bit number: sign-extend to a full word
 dx2=63
 and dx1,dx2
 if dx1<32 then SignExtendd6Ret
 dx2=65472 ; 65536-64
 add dx1,dx2
.SignExtendd6Ret
 return
;----
;.Divdx1dx2
;; dx1:=dx1/dx2
; dx3=65535 ; result = 0 if dx2>dx1
;.DivdLoop
; add dx3,c1
; sub dx1,dx2
; if dx1<50000 then DivdLoop
; dx1=dx3
; return
;;---

.DivdX1dX2
; dx1:=dx1 div dx2
 dx3=0 ; result
 if dx2=0 then DivdX1dX2End
.DivdX1dX2a
 sub dx1,dx2
 if dx1>32011 then DivdX1dX2End
 add dx3,c1
 goto DivdX1dX2a

.DivdX1dX2End
 dx1=dx3
 return
;----
.ExtractMap
; extract map from the "generated" raster object dv1
push ObjectNumber
 ObjectNUmber=dv1
 gosub @FindAndDecode
pop ObjectNumber

 dv1=4
 add dv1,header ; read ptr within StructureBuffer()
 dv3=size
 add dv3,header ; dv3=end ptr within StructureBuffer()
 dv2=0 ; write ptr within Map()
.EMLoop
 &dx1=StructureBuffer(dv1)
 dx6=15
 dx5=15
 dx4=15
 dx3=15
 and dx6,dx1
 asr dx1
 asr dx1
 asr dx1
 asr dx1
 and dx5,dx1
 asr dx1
 asr dx1
 asr dx1
 asr dx1
 and dx4,dx1
 asr dx1
 asr dx1
 asr dx1
 asr dx1
 and dx3,dx1
; now dx3,dx4,dx5,dx6 are leftmost..rightmost cells within
; this block
 if dv2>1000 then @EMExit
 &Map(dv2)=dx3
 add dv2,c2
 &Map(dv2)=dx4
 add dv2,c2
 &Map(dv2)=dx5
 add dv2,c2
 &Map(dv2)=dx6
 add dv2,c2
 add dv1,c2
 if dv1<dv3 then @EMLoop
.EMExit
 return
;---
